import React, {Component} from 'react';
import {btnSound, gameFailSound, gameWinSound, moveSound, peaceSound, playSound, startSound} from '@/utils/sounds-res';
import {boardColSize, boardRowSize,} from '@/utils/board-utils';
import styles from './index.less';
import SocketEvent from '@/service/event';
import User from '@/pages/board/user/index';
import {Modal, Toast} from 'antd-mobile';
import Win from '@/pages/board/win';
import {log} from '@/utils/log-utils';
import config from '@/config';
import * as boardCanvas from "@/utils/board-canvas-utils";
import * as headCanvas from "@/utils/head-canvas-utils";
import AdvancedSpin from "@/spinner";
import CheckWin from "@/utils/check-win";

/**
 * 棋盘
 */
class BoardPlatform extends Component {

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      text: null,
      /** -- 棋盘初始化时的一些状态 -- */
      gameMap: [],
      // 棋子被选中时外套一个盒子，此属性决定该盒子是否显示
      chessBox: {show: false, x: 0, y: 0},
      // 当前被选中的棋子
      selectedChess: {id: null, x: null, y: null},
      // 历史走过的点，数据：{gameMap: {}, chessBox: {}, chess: {}}
      historyMoveStep: [],
      /** -- 游戏进行时的一些状态 -- */
      // 落子方
      isRedMove: true,
      // 是否正在结算游戏
      handleBattleData: false,
      // 游戏是否结束
      gameOver: false,
      // 步数计数器
      stepCount: 0,
      // 游戏胜利后的得分情况
      gameScore: {'winScore': 0, 'failScore': 0},
      // 得分组件是否展示
      winShow: false,
      /** -- 用户信息 -- */
      user: null,
      enemy: null,
    };
    // 棋子颜色枚举
    this.red = 'red';
    this.black = 'black';

    // 事件回调id
    this.moveChessRespId = 'battleMoveChessRespId';
    this.gameWinRespId = 'battleGameWinRespId';
    this.syncBattleDataRespId = 'battleSyncBattleDataRespId';
    this.userTimeRespId = 'battleUserTimeRespId';
    this.sessionRecoverRespId = 'battleSessionRecoverRespId';

    this.unRegisterServices = [];
    this.unRegisterServices.push(this.moveChessRespId);
    this.unRegisterServices.push(this.gameWinRespId);
    this.unRegisterServices.push(this.syncBattleDataRespId);
    this.unRegisterServices.push(this.userTimeRespId);
    this.unRegisterServices.push(this.sessionRecoverRespId);
    this.unRegisterServices.push(this.kickUserId);
  }

  componentDidMount() {
    // 绑定节点
    boardCanvas.initBinds(this.boardRef);
    // 初始化游戏数据
    this.initGameData();
    // 事件监听
    this.startEventListen();
  }

  /**
   * 初始化游戏
   */
  initGameData = () => {
    const {userId, roomId, roomUser, roomEnemy} = this.props;
    log('进入棋盘页面，入参：', {userId, roomId, roomUser, roomEnemy});

    this.initBattleData(roomUser, roomEnemy);

    // 开启游戏计时
    this.startCountTime();

    playSound(startSound);
  };

  /**
   * 事件监听
   */
  startEventListen = () => {
    // 会话恢复结果通知(适用于在棋盘界面离线时间过久，又重新连接的情况，主要是要初始化用户的数据)
    SocketEvent.register('sessionRecoverRespApi', this.sessionRecoverRespId, resp => {
      log(`(对局)会话恢复结果通知返回：`, resp);
      const {roomUser, roomEnemy} = resp.data || {};
      // 仅处理对局类要恢复的数据
      if (resp.code === 'S000002') {
        if (roomUser && roomEnemy) {
          // 初始化对局数据
          this.initBattleData(roomUser, roomEnemy);
        } else {
          // 数据断连时，服务器没有返回对战数据，表示对局已经结束了
          Modal.alert('系统提示', '对局已经结束', [
            {
              text: '确认', onPress: () => {
                playSound(btnSound);
                this.props.goBack();
              },
            },
          ]);
        }
      }
    });

    // 对局倒计时监听
    SocketEvent.register('userTimeRespApi', this.userTimeRespId, resp => {
      log('对战倒计时返回：', resp);
      if (resp.code === 'success') {
        const {user, enemy} = this.state;
        const {userList} = resp.data || [];
        const userData = userList.find(item => item.userId === user.userId);
        if (userData) {
          user.stepTime = userData.stepTime;
          user.allTime = userData.allTime;
        }
        const enemyData = userList.find(item => item.userId === enemy.userId);
        if (enemyData) {
          enemy.stepTime = enemyData.stepTime;
          enemy.allTime = enemyData.allTime;
        }
        this.setState({user, enemy});
      }
    });

    // 自动同步房间的数据
    SocketEvent.register('syncBattleDataRespApi', this.syncBattleDataRespId, resp => {
      log('同步对战数据返回：', resp);
      if (resp.code === 'success') {
        // 返回的对象
        const {roomUser, roomEnemy} = resp.data;
        this.initBattleData(roomUser, roomEnemy);
      }
    });

    // 注册棋子移动的回调事件
    SocketEvent.register('moveChessRespApi', this.moveChessRespId, resp => {
      log('棋子移动，resp: ', resp);
      const {gameOver} = this.state;
      if (resp.code === 'success') {
        if (!gameOver) {
          const { to, chessFromUserId } = resp.data || {};
          // 处理棋子移动
          this.handleMakeChess(to);
        }
      }
    });

    // 游戏结束后的弹窗
    SocketEvent.register('gameWinRespApi', this.gameWinRespId, (resp) => {
      log('游戏结束，resp: ', resp);
      if (resp.code === 'success') {
        // 清除定时器
        clearInterval(this.intervalId);
        const {user} = this.state;
        const stepCount = resp.data.stepCount;
        const winColor = resp.data.isRedColorWin;
        const type = resp.data.type;

        // 对局在两步之类，不扣分
        let winTypeMsg = '';
        if (type === '0006') {
          playSound(peaceSound);
          Toast.show('和棋');
          winTypeMsg = '和棋';
        } else if (type === '0005') {
          playSound(peaceSound);
          Toast.show('双方议和');
          winTypeMsg = '和棋';
        } else if (stepCount <= 2) {
          playSound(peaceSound);
          Toast.show('对局在两步之内不计输赢');
          winTypeMsg = '和棋';
        } else if (type === '0002') {
          playSound(winColor === user.first ? gameWinSound : gameFailSound);
          Toast.show(`${winColor === user.first ? '对方' : '我方'}认输`);
          winTypeMsg = `${winColor === user.first ? '胜利' : '失败'}`;
        } else if (type === '0003') {
          playSound(winColor === user.first ? gameWinSound : gameFailSound);
          Toast.show(`${winColor === user.first ? '对方' : '我方'}逃跑判负`);
          winTypeMsg = `${winColor === user.first ? '胜利' : '失败'}`;
        } else if (type === '0004') {
          playSound(winColor === user.first ? gameWinSound : gameFailSound);
          Toast.show(`${winColor === user.first ? '对方' : '我方'}对局超时判负`);
          winTypeMsg = `${winColor === user.first ? '胜利' : '失败'}`;
        } else {
          playSound(winColor === user.first ? gameWinSound : gameFailSound);
          winTypeMsg = `${winColor === user.first ? '胜利' : '失败'}`;
        }
        // 游戏结束
        this.setState({
          gameOver: true,
          gameScore: {
            isRedColorWin: resp.data.isRedColorWin,
            winScore: resp.data.winScore,
            failScore: resp.data.failScore,
            winTypeMsg: winTypeMsg,
          },
          winShow: true,
          winColor,
          loading: false,
          handleBattleData: false,
        });
      }
    });

    /**
     * 被踢出房间
     */
    SocketEvent.register('kickUserRespApi', this.kickUserId, (resp) => {
      log('(对局页面)被房主踢出房间，resp: ', resp);
      if (resp.code === 'success') {
        const { userId, roomId } = resp.data;
        const { user: roomUser } = this.state;
        log('被房主踢出房间，用户信息为: ', roomUser);

        if(roomUser && roomUser.userId === userId && roomUser.roomId === roomId) {
          this.setState({ userIsKick: true });
          Modal.alert('系统提示', '您被房主请出了房间', [
            {
              text: '确认', onPress: () => {
                playSound(btnSound);
              },
            },
          ]);
        }
      } else {
        Toast.show(resp.msg);
      }
    });
  };

  /**
   * 初始化对战数据
   * @param roomUser
   * @param roomEnemy
   */
  initBattleData = (roomUser, roomEnemy) => {
    const { gameMap, isRedMove, chessBox} = roomUser;
    // 落子方地图
    boardCanvas.initMap(gameMap);
    boardCanvas.setChessBox(chessBox);
    this.setState({
      user: roomUser,
      enemy: roomEnemy,
      gameMap: gameMap,
      isRedMove: isRedMove,
      historyMoveStep: roomUser.historyMoveStep.length > 0 ? roomUser.historyMoveStep : [{
        gameMap: gameMap,
        lastChess: null,
        chessBox: {show: false},
      }],
    });
    // 我方先手时，提示走棋
    (roomUser.first && isRedMove) && Toast.show('请您走棋')
  };

  /**
   * 开启游戏计时
   */
  startCountTime = () => {
    log(`已开启游戏计时`);
    // 进行倒计时
    this.intervalId = setInterval(() => {
      const {user, enemy, isRedMove} = this.state;
      if (user && enemy) {
        // 计时方
        const roomUser = user.first === isRedMove ? user : enemy;
        const stepTime = roomUser.stepTime;
        const allTime = roomUser.allTime;
        // 客户端计时只是展示效果，实际计时由服务器完成
        // 所以当客户端计时小于0时不再进行任何操作，服务器计时结束后会自动判定输赢
        if (stepTime >= 0 && allTime >= 0) {
          // 局时和步时减一
          roomUser.stepTime = stepTime - 1;
          roomUser.allTime = allTime - 1;
          if (roomUser.userId === this.props.userId) {
            if (roomUser.stepTime === config.stepTimeoutTips) {
              Toast.show('请注意，步时即将超时');
            } else if (roomUser.allTime === config.allTimeoutTips) {
              Toast.show('请注意，局时即将超时');
            }
          }

          let processAllTime = roomUser.allTime > 0 ? roomUser.basicStepTime : roomUser.readSeconds;
          // 根据落子方更新数据
          if (user.first === isRedMove) {
            this.setState({user: roomUser});
            headCanvas.setProcess(user.userId, processAllTime, roomUser.stepTime);
          } else {
            this.setState({enemy: roomUser});
            headCanvas.setProcess(enemy.userId, processAllTime, roomUser.stepTime);
          }
        }
      }
    }, 1000);
  };

  /**
   * 棋盘被点击的事件
   */
  boardClick = e => {
    const {gameOver, user, isRedMove} = this.state;
    if (gameOver) {
      // 对局结束后再点击棋盘直接弹出对战结果
      this.setState({winShow: true});
      return false;
    } else if(user.first !== isRedMove) {
      Toast.show('请等待对方落子');
      return false;
    }

    // 将当前鼠标点击的坐标转成棋盘坐标
    const clickChess = this.getClickChess(e);
    if(this.handleMakeChess(clickChess)) {
      // 将棋子传送给对手
      this.sendChessDataToEnemy(clickChess)
    }
    return true;
  };



  /**
   * 落子
   * @param clickChess
   */
  handleMakeChess = (clickChess) => {
    const {gameMap, user, enemy, stepCount, isRedMove, historyMoveStep} = this.state;
    // 判断此位置是否已有棋子
    const existsChess = gameMap.find(chess => chess.x === clickChess.x && chess.y === clickChess.y);
    if(existsChess) {
      Toast.show('位置已有棋子');
      return false;
    }
    playSound(moveSound);

    // 棋子信息 & 棋子盒子
    const chess = {...clickChess, isBlackColor: isRedMove}
    const chessBox = {...chess, show: true }
    gameMap.push(chess)

    // 恢复双方倒计时
    user.stepTime = user.basicStepTime;
    enemy.stepTime = enemy.basicStepTime;

    // 走棋动画
    boardCanvas.moveChess(chess, gameMap);
    boardCanvas.setChessBox(chessBox);
    // 清空头像计时
    headCanvas.clearProcess((user.first === isRedMove ? user : enemy).userId);

    // 记录操作步骤
    historyMoveStep.push({
      gameMap: gameMap,
      lastChess: chess,
      chessBox: chessBox,
    });
    // 更新数据
    this.setState({
      stepCount: stepCount + 1,
      isRedMove: !isRedMove,
      gameMap,
      historyMoveStep
    });
    // 检查游戏是否结束
    if(CheckWin(gameMap, chess)) {
      this.setState({gameOver: true});
      headCanvas.destroyProcess();
    }
    return true;
  };

  /**
   * 发送棋子数据给对手
   */
  sendChessDataToEnemy = (clickChess) => {
    const {user, enemy, gameMap} = this.state;
    const toChessBox = {show: true, x: clickChess.x, y: clickChess.y};
    const toChess = gameMap.find(chess => chess.x === clickChess.x && chess.y === clickChess.y);

    // 将棋子发送给服务器
    SocketEvent.emit(this.props.socket, 'moveChessApi', {
      to: toChess,
      chessBox: toChessBox,
      roomId: this.props.roomId,
      battleId: this.props.battleId,
      userId: this.props.userId,
    }, resp => {
      if (resp.code === 'fail') {
        this.setState({loading: true, text: resp.msg, timePause: true});

        // 等待一段时间后，同步服务器数据
        setTimeout(() => {
          // 同步服务器的数据
          SocketEvent.emit(this.props.socket, 'syncBattleDataApi', {
            userId: this.props.userId,
            roomId: this.props.roomId,
            battleId: this.props.battleId,
          }, resp => {
            // 双方头像的倒计时重置
            headCanvas.resetProcess(enemy.userId);
            headCanvas.resetProcess(user.userId);

            this.setState({loading: false, timePause: false});
            if (resp.code !== 'success') {
              Toast.show(resp.msg);
              // 若同步也失败，则撤回刚才走的那步棋
              this.handleBackMove();
            }
          });
        }, 1000);
      }
    });
  };

  /**
   * 获取点击的坐标
   * @param e
   * @return {{x: (number|number), y: (number|number)}}
   */
  getClickChess = e => {
    const boardBoundRect = this.boardRef.getBoundingClientRect();

    const width = boardBoundRect.width;
    const height = boardBoundRect.height;
    // console.log('棋盘宽高数据：', width, height);

    // board元素距离浏览器的距离
    const marginTop = boardBoundRect.top;
    const marginLeft = boardBoundRect.left;
    // console.log('棋盘距离浏览器的距离：' + marginTop + ', ' + marginLeft);

    // 当前鼠标点击的位置
    const pageX = e.pageX;
    const pageY = e.pageY;
    // console.log('当前鼠标点击的位置：' + pageX + ', ' + pageY);

    // 当前鼠标点击的位置距离其顶点(0,0)的距离
    const offsetX = (pageX - marginLeft);
    const offsetY = (pageY - marginTop);
    // console.log('当前位置距离棋盘的顶点差：' + offsetX + ', ' + offsetY);

    const x = Math.floor(offsetX / (width / boardColSize));
    const y = Math.floor(offsetY / (height / boardRowSize));
    // console.log(`鼠标点击的坐标：${y}, ${x}`);

    return {
      x: y < 0 ? 0 : y < boardRowSize ? y : boardRowSize - 2,
      y: x < 0 ? 0 : x < boardColSize ? x : boardColSize - 2,
      id: this.state.isRedMove ? 'black' : "white",
    };
  };

  /**
   * 我方认输
   */
  userAdmitDefeat = () => {
    // 告知对方我方已主动认输
    SocketEvent.emit(this.props.socket, 'userAdmitDefeatApi', {
      userId: this.props.userId,
      battleId: this.props.battleId,
    }, resp => {
      if (resp.code === 'success') {
        this.setState({loading: true, text: '正在结算...', handleBattleData: true});
      } else {
        Toast.show(resp.msg);
      }
    });
  };

  /**
   * 处理悔棋
   */
  handleBackMove = () => {
    const {historyMoveStep, isRedMove, user, enemy} = this.state;
    if (historyMoveStep.length === 0) {
      Toast.show('空棋盘，无法悔棋');
    } else {
      if (historyMoveStep.length > 1) {
        // 移除节点
        const {lastChess} = historyMoveStep[historyMoveStep.length - 1];
        boardCanvas.removeChess(lastChess.id);
        historyMoveStep.pop();
      }
      // 如果最后只剩下一条记录则是原始棋盘，不直接弹出，而是取值
      const lastMove = historyMoveStep[historyMoveStep.length - 1];
      log(`悔棋，isRedMove: ${isRedMove}, 长度为：${historyMoveStep.length} 回退到：`, lastMove);
      this.setState({
        historyMoveStep,
        isRedMove: !isRedMove,
        gameMap: lastMove.gameMap,
        stepCount: historyMoveStep.length - 1,
      });
      // 设置盒子
      boardCanvas.setChessBox(historyMoveStep.length > 1 ? lastMove.chessBox : {show: false});
      // 清空头像计时
      headCanvas.clearProcess((user.first === isRedMove ? user : enemy).userId);
    }
  };

  componentWillUnmount() {
    headCanvas.destroyProcess();
    clearInterval(this.intervalId);
    SocketEvent.unRegister(this.unRegisterServices);
  }

  render() {
    const {user, enemy, winShow} = this.state;
    return (
      <div className={styles.bg}>
        <div
          className={styles.board}
          ref={ref => this.boardRef = ref}
          onClick={e => this.boardClick(e)}
        />

        {/*对局人物组件*/}
        <User
          playOne={enemy || {}}
          playTwo={user || {}}
          headCanvas={headCanvas}
          roomId={this.props.roomId}
          isRedMove={this.state.isRedMove}
          gameOver={this.state.gameOver}
          handleBattleData={this.state.handleBattleData}
          userId={this.props.userId}
          socket={this.props.socket}
          battleId={this.props.battleId}
          historyMoveStep={this.state.historyMoveStep}
          showGameOverWindow={() => this.setState({winShow: this.state.gameOver})}
          exitPk={() => this.userAdmitDefeat()}
          handleBackMove={() => this.handleBackMove()}
        />
        {/*游戏胜利时的弹窗*/}
        {winShow &&
        <Win
          user={this.state.user}
          enemy={this.state.enemy}
          gameScore={this.state.gameScore}
          goBack={() => this.props.goBack()}
          closeWin={() => this.setState({winShow: false})}/>
        }
        <AdvancedSpin text={this.state.text} show={this.state.loading}/>
      </div>
    );
  }

}

export default BoardPlatform;
